package org.lgiavedoni.android.franq.core.prediction.strategy.imp;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.lgiavedoni.android.franq.core.envent.dao.EventDao;
import org.lgiavedoni.android.franq.core.place.Place;
import org.lgiavedoni.android.franq.core.prediction.rule.PredictionRule;
import org.lgiavedoni.android.franq.core.prediction.rule.dao.PredictionRuleDAO;
import org.lgiavedoni.android.franq.core.prediction.rule.imp.PredictionRuleCheckinDay;
import org.lgiavedoni.android.franq.core.prediction.strategy.PredictionStrategy;
import org.lgiavedoni.android.franq.event.CheckInEvent;
import org.lgiavedoni.android.franq.util.date.DateUtil;

public class AnibalDayPredictionStrategy implements PredictionStrategy {

	private static final int MINUTES_PER_HOUR = 60; // Se que es obvio. Lo pongo
													// para
	// que sea mas clara la
	// matematica nomas
	private static final int FRACTION_OF_DAY = 24; // Horas en que se fracciona
													// el dia
	private static final int FRACTION_OF_HOUR = 15; // Minutos en los que se
													// fracciona
	// la hora

	private final EventDao eventDao = EventDao.inst;
	private final PredictionRuleDAO predictionRuleDAO = PredictionRuleDAO.inst;

	@Override
	public List<PredictionRule> generateNewRules() {
		List<PredictionRule> l = new ArrayList<PredictionRule>();

		ArrayList<CheckInEvent> events = new ArrayList<CheckInEvent>((List<CheckInEvent>) eventDao.getAllInstanceOf(CheckInEvent.class));

		// Hago un sort para un corte de control posterior
		Collections.sort(events, new Comparator<CheckInEvent>() {
			public int compare(CheckInEvent event1, CheckInEvent event2) {
				int result1 = event1.getPlace().compareTo(event2.getPlace());
				int result2 = Integer.valueOf(DateUtil.getField(event1.getCreationDate(), Calendar.DAY_OF_WEEK)).compareTo(
						Integer.valueOf(DateUtil.getField(event1.getCreationDate(), Calendar.DAY_OF_WEEK)));

				return result1 != 0 ? result1 : result2;
			}
		});

		int index = 0;
		while (index < events.size()) {
			Place place = events.get(index).getPlace();
			while (index < events.size() && events.get(index).getPlace().equals(place)) {
				int day = DateUtil.getField(events.get(index).getCreationDate(), Calendar.DAY_OF_WEEK);
				int[] occurrences = new int[FRACTION_OF_DAY * MINUTES_PER_HOUR / FRACTION_OF_HOUR];
				while (index < events.size() && events.get(index).getPlace().equals(place) && DateUtil.getField(events.get(index).getCreationDate(), Calendar.DAY_OF_WEEK) == day) {
					int hindex = DateUtil.getField(events.get(index).getCreationDate(), Calendar.HOUR_OF_DAY);
					int mindex = Double.valueOf(Math.ceil((double) DateUtil.getField(events.get(index).getCreationDate(), Calendar.MINUTE) / (double) FRACTION_OF_HOUR)).intValue();

					occurrences[hindex * MINUTES_PER_HOUR / FRACTION_OF_HOUR + mindex]++;
					index++;
				}

				Integer start = null;
				Integer end = null;
				for (int i = 0; i < occurrences.length; i++) {
					end = occurrences[i] == 0 && start != null ? Integer.valueOf(i - 1) : (occurrences[i] > 0 && i == occurrences.length - 1 ? Integer.valueOf(i) : null);
					start = occurrences[i] > 0 && start == null ? Integer.valueOf(i) : start;

					if (start != null && end != null) {

						double value = 0;
						int sum = this.sum(occurrences, start.intValue(), end.intValue());
						for (int j = start.intValue(); j <= end.intValue(); j++) {
							value += ((double) j * (double) FRACTION_OF_HOUR) / MINUTES_PER_HOUR * ((double) occurrences[j] / (double) sum);
							// occurrences[j] = 0;
						}
						// occurrences[Double.valueOf((Math.floor(value) +
						// (value - Math.floor(value)) * MINUTES_PER_HOUR /
						// FRACTION_OF_HOUR)).intValue()] = sum;

						PredictionRule pr = new PredictionRuleCheckinDay(day, Double.valueOf(Math.floor(value)).intValue(), Double.valueOf(
								Math.floor((value - Math.floor(value)) * MINUTES_PER_HOUR / FRACTION_OF_HOUR) * FRACTION_OF_HOUR).intValue(), place);
						pr.setOccurrence(sum);
						pr.setNoOccurrence(Math.abs(DateUtil.countDaysOfTheWeek(events.get(0).getCreationDate(), day) - sum));
						l.add(pr);

						start = null;
						end = null;

					}
				}

			}

		}

		return l;
	}

	@Override
	public void updatePredictionRuleOcurrences() {
		// TODO Auto-generated method stub
	}

	// Metodos privados
	protected int maximum(int[] values) {
		int result = 0;
		for (int i = 0; i < values.length; i++) {
			result = values[i] > values[result] ? i : result;
		}
		return result;
	}

	protected int sum(int[] values, int from, int until) {
		int result = 0;
		for (int i = from; i <= until; i++) {
			result += values[i];
		}
		return result;
	}

}
